﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Dynamic;
using System.Collections;
using Umbraco.Forms.Core;
using System.Linq.Dynamic;
using System.Reflection;
using umbraco.MacroEngines;

namespace ContourStrikesAgain.Razor
{
    public class DynamicRecordList : DynamicObject, IEnumerable
    {
        public IEnumerable<DynamicRecord> Items { get; set; }

        public DynamicRecordList()
        {
            Items = new List<DynamicRecord>();
        }

        public DynamicRecordList(IEnumerable<DynamicRecord> items)
        {
            List<DynamicRecord> list = items.ToList();
            list.ForEach(node => node.ownerList = this);
            Items = list;
        }

        public DynamicRecordList(IEnumerable<Record> items)
        {
            List<DynamicRecord> list = items.Select(x => new DynamicRecord(x)).ToList();
            list.ForEach(node => node.ownerList = this);
            Items = list;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            var name = binder.Name;

            if (name == "Where")
            {
                string predicate = args.First().ToString();
                var values = args.Skip(1).ToArray();
                result = new DynamicRecordList(this.Where<DynamicRecord>(predicate, values).ToList());
                return true;
            }
            if (name == "OrderBy")
            {
                result = new DynamicRecordList(this.OrderBy<DynamicRecord>(args.First().ToString()).ToList());
                return true;
            }

            try
            {

                result = Items.GetType().InvokeMember(binder.Name,
                                                  System.Reflection.BindingFlags.Instance |
                                                  System.Reflection.BindingFlags.Public |
                                                  System.Reflection.BindingFlags.NonPublic |
                                                  System.Reflection.BindingFlags.GetProperty,
                                                  null,
                                                  Items,
                                                  args);
                return true;
            }
            catch (MissingMethodException)
            {
                try
                {
                    result = Items.GetType().InvokeMember(binder.Name,
                                                   System.Reflection.BindingFlags.Instance |
                                                   System.Reflection.BindingFlags.Public |
                                                   System.Reflection.BindingFlags.NonPublic |
                                                   System.Reflection.BindingFlags.Static |
                                                   System.Reflection.BindingFlags.InvokeMethod,
                                                   null,
                                                   Items,
                                                   args);
                    return true;
                }
                catch (MissingMethodException)
                {

                    try
                    {
                        result = ExecuteExtensionMethod(args, name, false);
                        return true;
                    }
                    catch (TargetInvocationException)
                    {
                        result = new DynamicRecord();
                        return true;
                    }

                    catch
                    {
                        result = null;
                        return false;
                    }

                }


            }
            catch
            {
                result = null;
                return false;
            }

        }

        private object ExecuteExtensionMethod(object[] args, string name, bool argsContainsThis)
        {
            object result = null;

            MethodInfo methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(IEnumerable<DynamicRecord>), args, name, false);
            if (methodToExecute == null)
            {
                methodToExecute = ExtensionMethodFinder.FindExtensionMethod(typeof(DynamicRecordList), args, name, false);
            }
            if (methodToExecute != null)
            {
                if (methodToExecute.GetParameters().First().ParameterType == typeof(DynamicRecordList))
                {
                    var genericArgs = (new[] { this }).Concat(args);
                    result = methodToExecute.Invoke(null, genericArgs.ToArray());
                }
                else
                {
                    var genericArgs = (new[] { Items }).Concat(args);
                    result = methodToExecute.Invoke(null, genericArgs.ToArray());
                }
            }
            else
            {
                throw new MissingMethodException();
            }
            if (result != null)
            {

                if (result is IEnumerable<DynamicRecord>)
                {
                    result = new DynamicRecordList((IEnumerable<DynamicRecord>)result);
                }

            }
            return result;
        }

        public IEnumerator GetEnumerator()
        {
            return Items.GetEnumerator();
        }

        private IQueryable<T> Where<T>(string predicate, params object[] values)
        {
            return ((IQueryable<T>)Items.AsQueryable()).Where(predicate, values);
        }

        private IQueryable<T> OrderBy<T>(string key)
        {
            return ((IQueryable<T>)Items.AsQueryable()).OrderBy(key);
        }
    }
}